#pragma once 

#include "online.hpp"
#include "db.hpp" 
#include<vector> 
#include<unordered_map>

#define BOARD_ROWS 15 
#define BOARD_COLS 15
#define WHITE_CHESS 1 
#define BLACK_CHESS 2

typedef enum {GAME_START , GAME_OVER} room_statu;
class room
{
private:
  int _room_id; //房间id 
  room_statu _statu; //房间状态  
  int _player_count;  //房间玩家数量
  int _white_id;  //白棋选手id
  int _black_id;  //黑棋选手id
  user_table* _ut; //用户信息表
  online_manager* _om;  //消息管理
  std::vector<std::vector<int>> _board; //棋盘 

private:
  
  bool five(int row ,int col , int row_off , int col_off , int color)
  {
    int count = 1 ; 
    int search_row = row + row_off ; 
    int search_col = col + col_off ; 
    //检索前一半
    while(search_row >= 0 && search_row < BOARD_ROWS && 
        search_col >=0 && search_col < BOARD_COLS && 
        _board[search_row][search_col] == color)
    {
      count++;
      search_row += row_off; 
      search_col += col_off;
    }
    
    //检索后一半
    search_row = row - row_off ; 
    search_col = col - col_off ; 
    while(search_row >= 0 && search_row < BOARD_ROWS && 
        search_col >=0 && search_col < BOARD_COLS && 
        _board[search_row][search_col] == color)
    {
      count++;
      search_row -= row_off; 
      search_col -= col_off;
    }
    return (count >= 5);
  }

  int check_win(int row ,int col , int color)
  {
    //任意一方向达到五子
    if (five(row,col,1,0,color) ||
    five(row,col,0,1,color)  ||
    five(row,col,-1,-1,color) || 
    five(row,col,-1,1,color) )
    {
      int winner_id = color == WHITE_CHESS ? _white_id : _black_id; 
      return winner_id; 
    }
    return 0;
  }
  
  void game_over(int winner)
  {
    int loser = winner == _white_id ? _black_id : _white_id; 
    _ut->lose(loser);
    _ut->win(winner);
    _statu = GAME_OVER; //游戏状态结束
  }

public:
  room(int room_id , user_table* ut , online_manager* om):_room_id(room_id), 
  _statu(GAME_START),_player_count(0),_ut(ut), _om(om),
  _board(BOARD_ROWS,std::vector<int>(BOARD_COLS,0))
  {
    DLOG("%d room has been created!",_room_id);
  }
  ~room(){
    DLOG("%d room has been destroied!",_room_id);
  }

  int id() { return _room_id; } 
  room_statu statu() { return _statu; }
  int player_count() {return _player_count;}
  void add_white_player(int uid) {_white_id = uid ; _player_count++;}
  void add_black_player(int uid) {_black_id = uid ; _player_count++;}
  int get_white_uid() {return _white_id;}  
  int get_black_uid() {return _black_id;}  

  //处理下棋动作
  Json::Value handler_chess(Json::Value& req){
     Json::Value resp = req; 
    //1.请求的房间号是否与当前房间号匹配 

     //2.判断两个房间是否都在线，如果有掉线那么则另一方获胜 
     int chess_row = req["row"].asInt(); //获取请求中要下棋的坐标y  
     int chess_col = req["col"].asInt(); //获取请求中要下棋的坐标x 
     int uid = req["uid"].asInt(); //获取当前操作的用户id 
     if(_om->is_in_game_room(_white_id) == false) 
     {
       //如果白棋玩家掉线      
       resp["result"] = true; 
       resp["reason"] = "对方掉线，您直接获胜！"; 
       resp["winner"] = _black_id; //白棋掉线，黑棋获胜
       return resp ; 
     }

     //如果黑棋玩家掉线      
     if(_om->is_in_game_room(_black_id) == false) 
     {
       resp["result"] = true; 
       resp["reason"] = "对方掉线，您直接获胜！"; 
       resp["winner"] = _white_id; //黑棋掉线，白棋获胜
       return resp ; 
     }

     //3.判断下棋的坐标是否合理  
     if(_board[chess_row][chess_col] != 0) //玩家下棋的坐标已经有棋子了
     {
        resp["result"] = false;
        resp["reason"] = "该位置已经有棋子了"; 
        return resp; 
     }
     //坐标合法 ,修改棋盘 
     int cur_color = uid == _white_id ? WHITE_CHESS : BLACK_CHESS ;  
     _board[chess_row][chess_col] = cur_color;  
     
     //4.判读是否有玩家胜利 
     int winner_id = check_win(chess_row,chess_col,cur_color);
     if(winner_id != 0)
     {
         resp["reason"] = "五星连珠，您已获胜!"; 
     }
     resp["result"] = true; 
     resp["winner"] = winner_id; 
     return resp ;  
  } 
  //处理聊天动作
  Json::Value handler_chat(Json::Value& req){
    Json::Value resp = req; 
    //检测房间是否一致

    //检测消息是否包含敏感词 
    std::string msg = resp["message"].asString();
    size_t pos = msg.find("操你妈");
    if(pos != std::string::npos )
    {
      //包含敏感词 
      resp["result"] = false; 
      resp["uid"] = req["uid"];
      resp["reason"] = "您发表的内容包含敏感词"; 
      return resp; 
    }

    //广播消息 
    resp["result"] = true;
    resp["uid"] = req["uid"];
    return resp;
  } 
  
  //处理玩家退出动作
  void handler_exit(int uid){
    Json::Value resp;
    DLOG("进入玩家退出处理函数");
    if(_statu == GAME_START )
    {
       int winner = uid == _white_id ? _black_id : _white_id;
       resp["optype"] = "put_chess"; 
       resp["result"] = true; 
       resp["reason"] = "对方掉线，您直接获胜！"; 
       resp["room_id"] = _room_id; 
       resp["uid"] = uid; 
       resp["row"] = -1; 
       resp["col"] = -1; 
       resp["winner"] =  winner; //白棋掉线，黑棋获胜
       game_over(winner);
       broadcast(resp);
    }
    _player_count--; 
  } 

  //选择处理哪种请求 
  void handler_request(Json::Value& req){
    //1.校验房间匹配 
    Json::Value resp; 
    int room_id = req["room_id"].asInt(); 
    if(room_id != _room_id)
    {
      resp["optype"] = req.asString();
      resp["result"]  = false; 
      resp["reason"] = "房间不匹配"; 
      broadcast(resp);
      return ;
    }
    
    //2.根据optype类型决定调用哪个处理函数 
    std::string opty = req["optype"].asString(); 
    if(opty == "put_chess")
    {
      DLOG("进入下棋检测");
      resp = handler_chess(req); //处理下棋 
      if(resp["winner"].asInt() != 0 )
      {
        //有人赢了 
        int winner_id = resp["winner"].asInt();
        game_over(winner_id );
      }
    }else if (opty == "chat")
    {
        resp = handler_chat(req);
    } else 
    {
      //ERROR
        resp["optype"] = req["optype"].asString(); 
        resp["result"] = false ; 
        resp["reason"] = "未知请求错误";
    }
    //广播消息
    broadcast(resp); 
  } 

  //广播信息 
  void broadcast(Json::Value& rsp){
    //1.对要响应的信息进行序列化 
    std::string body; 
    UtilJson::serialize(rsp,body); 

    //2.获取要广播的连接对象并发送响应信息
    wsserver_t::connection_ptr wcon = _om->get_conn_from_room(_white_id);  //获取白棋玩家连接对象 
    if(wcon != nullptr)
    {
      //获取连接成功 
      DLOG("白棋胜利，广播body");
      wcon->send(body);
    }
    wsserver_t::connection_ptr bcon = _om->get_conn_from_room(_black_id); 
    if(bcon != nullptr)
    {
      DLOG("黑棋胜利，广播body");
      bcon->send(body);
    }
  }

};

using room_ptr = std::shared_ptr<room>; 

class room_manage
{
private: 
  int _next_rid; //房间计数器 
  std::mutex _mutex; //锁 
  user_table* _ut; //数据管理模块 
  online_manager* _om; //在线管理模块
  std::unordered_map<int,room_ptr> _rooms; //房间ID和房间的哈希映射 
  std::unordered_map<int,int> _users; //用户ID和房间ID的映射 

public:
  room_manage(user_table* ut , online_manager* om)
  : _next_rid(1) , _ut(ut),_om(om)
  {
    DLOG("房间管理器初始化完毕！");
  }
  ~room_manage(){
    DLOG("房间管理器销毁完毕！");
  }
  //为2个用户创建一个房间
  room_ptr create_room(int uid1, int uid2)
  { 
    //两个用户必须都在大厅中才能分配房间 
    if(!_om->is_in_game_hall(uid1))
    {
      DLOG("%d 用户不在大厅中！",uid1);
      return room_ptr();
    }
    if(!_om->is_in_game_hall(uid2))
    {
      DLOG("%d 用户不在大厅中！",uid2);
      return room_ptr();
    }

    //两个用户都在大厅中,创建房间
    std::unique_lock<std::mutex> lock(_mutex); //_rooms和_users是临界资源，操作时要加锁
    room_ptr rp(new room(_next_rid,_ut,_om));
    //把2个用户添加进房间 
    rp->add_black_player(uid1);
    rp->add_white_player(uid2);
    //将房间管理起来 
    _rooms.insert(std::make_pair(_next_rid,rp));  //房间ID与房间映射 
    _users.insert(std::make_pair(uid1,_next_rid));
    _users.insert(std::make_pair(uid2,_next_rid));
    
    _next_rid++;
    //返回房间信息 
    return rp;
  }
  
  //通过房间ID获取房间信息
  room_ptr get_room_by_rid(int room_id)
  {
    //加锁 
    std::unique_lock<std::mutex> lock(_mutex); 
    auto rit = _rooms.find(room_id); 
    if(rit == _rooms.end())
    {
      //没找到 
      return room_ptr();
    }
    return rit->second;
  }

  //通过用户id获取房间信息
  room_ptr get_room_by_uid(int uid)
  {
    //加锁
    std::unique_lock<std::mutex> lock(_mutex); 
    
    //通过用户id找到房间id 
    auto uit = _users.find(uid); 
    if(uit == _users.end())
    {
      //没找到
      return room_ptr(); 
    }
    
    //通过房间id找到房间 
    auto rit = _rooms.find(uit->second); 
    if(rit == _rooms.end())
    {
      return room_ptr();
    }
    return rit->second;
  }
  
  //通过房间id销毁房间
  void remove_room(int room_id)
  {
    //通过房间id获取房间信息 
    room_ptr rp = get_room_by_rid(room_id); 
    if(rp == nullptr)
    {
      return; 
    }
    //通过房间信息获取所有用户id 
    int uid1 = rp->get_black_uid();
    int uid2 = rp->get_white_uid(); 
    //移除用户 
    std::unique_lock<std::mutex> lock(_mutex); //加锁 
    _users.erase(uid1);
    _users.erase(uid2);

    //移除房间 
    _rooms.erase(room_id);
  }

  //移除房间里的用户
  void remove_room_user(int uid)
  {

    DLOG("进入移除用户函数");
    //通过用户id获取房间信息 
    room_ptr rp = get_room_by_uid(uid); 
    if(rp.get() == nullptr)
    {
      //房间不存在 
      return; 
    }

    rp->handler_exit(uid); //处理uid用户退出 
    
    if(rp->player_count() == 0)
    {
      //房间玩家为0，销毁房间 
      remove_room(rp->id());
    }
  }


};
